﻿/*************************************
 * Creator:SunnyPaine
 * DateTime:2022/8/22 15:21:52
 * Description:<Description>
 * CopyRight:
 * ***********************************/

using Newtonsoft.Json;
using SunnyPaine.Autofac.Extension.Stereotype.Attributes;
using SunnyPaine.Autofac.Extension.Stereotype.Entities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace SunnyPaine.Autofac.Extension.Stereotype
{
    /// <summary>
    /// 程序集或类型的扫描器。
    /// </summary>
    internal class Scanner
    {
        /// <summary>
        /// 是否使用接口标记符 InjectAttribute 进行扫描。
        /// </summary>
        private readonly bool useInterfaceInject = false;
        /// <summary>
        /// 包含例外的接口类型。如果接口类型未被 InjectAttribute 标记，但存在于例外接口类型中，也会被扫描注册。
        /// </summary>
        private readonly Dictionary<string, string> containInterfaceDict;

        /// <summary>
        /// 使用默认参数创建 SunnyPaine.Autofac.Extension.Stereotype.Scanner 的实例。
        /// </summary>
        internal Scanner() : this(false)
        { }

        /// <summary>
        /// 使用指定的参数创建 <see cref="Scanner"/> 的实例。
        /// </summary>
        /// <param name="useInterfaceInject">是否使用接口标记。
        /// <para>true：autofac进行自动扫描与注册实例时，只会对被标记的的接口进行实例化或引用。</para>
        /// <para>false：autofac进行自动扫描与注册实例时，将对类所有实现的接口（包括父级接口）进行实例化或引用。</para>
        /// </param>
        internal Scanner(bool useInterfaceInject) : this(null, useInterfaceInject)
        { }

        /// <summary>
        /// 使用指定的参数创建 <see cref="Scanner"/> 的实例。
        /// </summary>
        /// <param name="containInterfaceTypes">包含在内的接口类型。将用于实例化或引用。</param>
        /// <param name="useInterfaceInject">是否使用接口标记。
        /// <para>true：autofac进行自动扫描与注册实例时，只会对被标记的的接口进行实例化或引用。</para>
        /// <para>false：autofac进行自动扫描与注册实例时，将对类所有实现的接口（包括父级接口）进行实例化或引用。</para>
        /// </param>
        internal Scanner(List<Type> containInterfaceTypes, bool useInterfaceInject = false)
        {
            this.containInterfaceDict = new Dictionary<string, string>();
            this.useInterfaceInject = useInterfaceInject;

            //处理包含在内的接口类型
            if (containInterfaceTypes != null)
            {
                foreach (Type item in containInterfaceTypes)
                {
                    this.containInterfaceDict.Add(item.Name, item.Assembly.FullName);
                }
            }
        }

        /// <summary>
        /// 扫描组件。
        /// </summary>
        /// <returns></returns>
        internal List<InstanceInfo> Scan()
        {
            List<string> files = this.GetAssemblyFiles();
            List<InstanceInfo> iis = new List<InstanceInfo>();
            foreach (string file in files)
            {
                Assembly assembly = Assembly.LoadFile(file);
                Type[] types = assembly.GetTypes().Where(p => !p.IsInterface && !p.IsAbstract && p.IsPublic).ToArray();
                foreach (Type type in types)
                {
                    //配置类
                    ConfigureAttribute configureAttribute = type.GetCustomAttribute<ConfigureAttribute>();
                    if (configureAttribute != null)
                    {
                        iis.Add(this.ParseAttribute(type, configureAttribute.Name, configureAttribute.Sort,
                            ComponentType.Configure, configureAttribute.Scope));
                        continue;
                    }

                    //处理数据层
                    DataAccessAttribute accessAttribute = type.GetCustomAttribute<DataAccessAttribute>();
                    if (accessAttribute != null)
                    {
                        iis.Add(this.ParseAttribute(type, accessAttribute.Name, accessAttribute.Sort,
                            ComponentType.DataAccess, accessAttribute.Scope));
                        continue;
                    }

                    //处理业务层
                    ServiceAttribute serviceAttribute = type.GetCustomAttribute<ServiceAttribute>();
                    if (serviceAttribute != null)
                    {
                        iis.Add(this.ParseAttribute(type, serviceAttribute.Name, serviceAttribute.Sort,
                            ComponentType.Service, serviceAttribute.Scope));
                        continue;
                    }

                    //处理控制层
                    ControllerAttribute controllerAttribute = type.GetCustomAttribute<ControllerAttribute>();
                    if (controllerAttribute != null)
                    {
                        iis.Add(this.ParseAttribute(type, controllerAttribute.Name, controllerAttribute.Sort,
                            ComponentType.Controller, controllerAttribute.Scope));
                        continue;
                    }
                }
            }

            return iis;
        }

        private List<string> GetAssemblyFiles()
        {
            string fileAutofac = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "autofac.json");
            List<string> fileInfos = null;
            if (!File.Exists(fileAutofac))
            {
                System.Diagnostics.Debug.WriteLine("警告：指定的文件 autofac.json 不存在，将执行全文见扫描，这会大量增加扫描耗时。");
            }
            else
            {
                string json = File.ReadAllText(fileAutofac);
                fileInfos = JsonConvert.DeserializeObject<List<string>>(json);
                if (fileInfos.Count == 0)
                {
                    System.Diagnostics.Debug.WriteLine("警告：指定的文件 autofac.json 未配置扫描的组件，将执行全文见扫描，这会大量增加扫描耗时。");
                }
            }

            List<string> files = new List<string>();
            if (fileInfos != null)
            {
                //处理autofac的通配符
                foreach (string item in fileInfos)
                {
                    if (item.EndsWith("*"))
                    {
                        string tmp = item.Replace("*", "");
                        string prefix = tmp;
                        if (tmp.Contains("/") || tmp.Contains("\\"))
                        {
                            string[] splits = tmp.Split(new char[] { '/', '\\' });
                            prefix = splits[splits.Length - 1];
                        }

                        string _path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, tmp);
                        string dir = Path.GetDirectoryName(_path);

                        bool func(string p)
                        {
                            string fileName = Path.GetFileName(p);
                            return (fileName.EndsWith(".dll") || fileName.EndsWith(".exe")) && fileName.StartsWith(prefix);
                        }
                        files.AddRange(Directory.GetFiles(dir).Where(func).ToList());
                    }
                    else
                    {
                        files.Add(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, item));
                    }
                }
            }
            else
            {
                Func<string, bool> func = (p) =>
                {
                    FileInfo fi = new FileInfo(p);
                    return !fi.Name.StartsWith("System")
                        && !fi.Name.StartsWith("Microsoft")
                        && !"SqlSugar.dll".Equals(fi.Name)
                        && !"Autofac.dll".Equals(fi.Name)
                        && !"Castle.Core.dll".Equals(fi.Name)
                        && (".exe".Equals(Path.GetExtension(p)) || ".dll".Equals(Path.GetExtension(p)));
                };
                IEnumerable<string> _files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory)
                                                      .Where(p => func(p));
                files.AddRange(_files);
            }
            return files;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type">组件的类型。</param>
        /// <param name="aliasName">组件的实例的别名。</param>
        /// <param name="sort">优先级。</param>
        /// <param name="componentType">组件的类型。</param>
        /// <param name="scope">实例是否局部。true：局部；false：单例。</param>
        /// <returns></returns>
        private InstanceInfo ParseAttribute(Type type, string aliasName, uint sort, ComponentType componentType, bool scope)
        {
            InstanceInfo ii = new InstanceInfo();
            ii.ComponentType = componentType;
            ii.Sort = sort;
            ii.Single = !scope;

            //解析配置类。
            if (componentType == ComponentType.Configure)
            {
                object objConfigure = Activator.CreateInstance(type);
                MethodInfo[] mis = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                foreach (MethodInfo mi in mis)
                {
                    BeanAttribute beanAttribute = mi.GetCustomAttribute<BeanAttribute>();
                    if (beanAttribute == null)
                    {
                        continue;
                    }
                    if (!mi.Name.StartsWith("Get"))
                    {
                        Console.WriteLine("配置类中被 BeanAttribute 标记的方法，方法名称必须以 Get 开头，例如 GetRedisTemplate()。");
                        continue;
                    }
                    ParameterInfo[] parameterInfos = mi.GetParameters();
                    if (parameterInfos != null && parameterInfos.Length > 0)
                    {
                        Console.WriteLine("配置类中被 BeanAttribute 标记的方法不允许存在参数。");
                        continue;
                    }
                    if (beanAttribute.Name == null)
                    {
                        ii.Name = mi.Name;
                    }
                    else
                    {
                        ii.Name = beanAttribute.Name.Replace("Get", string.Empty);
                    }
                    ii.Instance = mi.Invoke(objConfigure, null);
                    ii.InterfaceTypes.Add(mi.ReturnType);
                }

                PropertyInfo[] pis = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic);
                foreach (PropertyInfo pi in pis)
                {
                    BeanAttribute beanAttribute = pi.GetCustomAttribute<BeanAttribute>();
                    if (beanAttribute == null)
                    {
                        continue;
                    }
                    if (beanAttribute.Name == null)
                    {
                        ii.Name = beanAttribute.Name;
                    }
                    else
                    {
                        ii.Name = pi.Name;
                    }
                    ii.Instance = pi.GetValue(objConfigure);
                    ii.InterfaceTypes.Add(pi.PropertyType);
                }
            }
            //解析其它常规类。
            else
            {
                ii.ImplementType = type;

                Type[] interfaces = type.GetInterfaces();
                if (this.useInterfaceInject)
                {
                    foreach (Type item in interfaces)
                    {
                        if (this.containInterfaceDict.ContainsKey(item.Name)
                            && this.containInterfaceDict[item.Name].Equals(item.Assembly.FullName))
                        {
                            ii.InterfaceTypes.Add(item);
                            continue;
                        }
                        InjectAttribute injectAttribute = item.GetCustomAttribute<InjectAttribute>();
                        if (injectAttribute == null)
                        {
                            continue;
                        }
                        ii.InterfaceTypes.Add(item);
                    }
                }
                else
                {
                    ii.InterfaceTypes.AddRange(interfaces);
                }

                if (aliasName != null)
                {
                    ii.Name = aliasName;
                }
                else if (type.IsGenericType)
                {
                    int index = type.Name.IndexOf('`');
                    ii.Name = type.Name.Substring(0, index);
                }
                else
                {
                    ii.Name = type.Name;
                }
            }
            return ii;
        }
    }
}
